home *** CD-ROM | disk | FTP | other *** search
-
- (*
- A unit to implement FULL ANSI output. Useful for a BBS or DOOR program
- where you would want to send string out over the modem. Simply call
- your modem routine to :
-
- SENDSTRING(port,ANSIGoToXY(1,1))
-
- Would reposition the cursor on the remote terminal. Get the idea ??
-
- The thing will EVEN play ANSI music !!
-
- Gayle Davis 1/24/94
-
- 1) Added allowance for "esc[M " as a valid music prefix. It is used
- occasionally.
-
- 2) Changed the effect of "esc[0m" from "NormVideo" to "textattr:=7",
- which is what "esc[0m" literally means. NormVideo just restores
- startup colors, which could be anything.
-
- 3) Added "HighVideo" line to take effect *immediately* when "esc[1m"
- ("Bold") is encountered. Otherwise, "esc[1m" by itself would not
- activate "Bold".
-
- 4) Changed "{blink on}" from "5 : textattr := textattr + blink;"
- . "5 : textattr := textattr or blink;"
- . ^^
- The "blink ON" was turning blink OFF when blink was turned ON
- with blink already ON.
-
- 5) Added "textattr and blink" to preserve blink status in the
- "{general foregrounds}" subroutine.
-
- 6) Changed default tempo assignment from "Min1:=120" to "Min1:=120/4"
- in order to be consistent with the way the unit deals with tempo.
-
- 7) Added an initialization line of "TextAttr:=7;" to allow for the
- fact that some ANSI artists assume that the screen is normal white
- on black to start with. (My screen is NOT that color!)
-
- DAVID DANIEL ANDERSON
- 09/08/94
-
- *)
-
- UNIT AnsiIO;
-
- INTERFACE
-
- USES
- CRT,
- Graph3; { GRAPH3.TPU is included in the BORLAND distribution diskettes }
-
- FUNCTION ANSIClrScr : string;
- FUNCTION ANSIClrEol : string;
- FUNCTION ANSIGotoXY(X, Y : word) : string;
- FUNCTION ANSIUp(Lines : word) : string;
- FUNCTION ANSIDown(Lines : word) : string;
- FUNCTION ANSIRight(Cols : word) : string;
- FUNCTION ANSILeft(Cols : word) : string;
- FUNCTION ANSIColor(Fg, Bg : integer) : string;
- FUNCTION ANSIMusic(s : string) : string;
- PROCEDURE ANSIWrite(s : string);
- PROCEDURE ANSIWriteLn(s : string);
-
- IMPLEMENTATION
-
- CONST
- ColorArray : array[0..7] of integer = (0,4,2,6,1,5,3,7);
-
- VAR
- Bold, TruncateLines : boolean;
- Vari, Octave, Numb : integer;
- Test, Dly, Intern, DlyKeep : longInt;
- Flager, ChartoPlay : char;
- Typom, Min1, Adder : real;
-
- {****************************************************************************}
- {*** ***}
- {*** Function that returns the ANSI code for a Clear Screen. ***}
- {*** ***}
- {****************************************************************************}
- FUNCTION ANSIClrScr : string;
- BEGIN
- ANSIClrScr := #27+'[2J';
- END;
-
- {****************************************************************************}
- {*** ***}
- {*** Function that returns the ANSI code for a Clear to End of Line. ***}
- {*** ***}
- {****************************************************************************}
- FUNCTION ANSIClrEol : string;
- BEGIN
- ANSIClrEol := #27+'[K';
- END;
-
- {****************************************************************************}
- {*** ***}
- {*** Function that returns the ANSI code to move the cursor to (X,Y). ***}
- {*** ***}
- {****************************************************************************}
- FUNCTION ANSIGotoXY(X, Y : word) : string;
- VAR
- XStr, YStr : string;
-
- BEGIN
- str(X,XStr);
- str(Y,YStr);
- ANSIGotoXY := #27+'['+YStr+';'+XStr+'H';
- END;
-
- {****************************************************************************}
- {*** ***}
- {*** Function that returns the ANSI code to move the cursor up "Lines" ***}
- {*** number of lines. ***}
- {*** ***}
- {****************************************************************************}
- FUNCTION ANSIUp(Lines : word) : string;
- VAR
- LinesStr : string;
-
- BEGIN
- str(Lines,LinesStr);
- ANSIUp := #27+'['+LinesStr+'A';
- END;
-
- {****************************************************************************}
- {*** ***}
- {*** Function that returns the ANSI code to move the cursor down "Lines" ***}
- {*** number of lines. ***}
- {*** ***}
- {****************************************************************************}
- FUNCTION ANSIDown(Lines : word) : string;
- VAR
- LinesStr : string;
-
- BEGIN
- str(Lines,LinesStr);
- ANSIDown := #27+'['+LinesStr+'B';
- END;
-
- {****************************************************************************}
- {*** ***}
- {*** Function that returns the ANSI code to move the cursor "Cols" ***}
- {*** positions forward. ***}
- {*** ***}
- {****************************************************************************}
- FUNCTION ANSIRight(Cols : word) : string;
- VAR
- ColsStr : string;
-
- BEGIN
- str(Cols,ColsStr);
- ANSIRight := #27+'['+ColsStr+'C';
- END;
-
- {****************************************************************************}
- {*** ***}
- {*** Function that returns the ANSI code to move the cursor "Cols" ***}
- {*** positions backward. ***}
- {*** ***}
- {****************************************************************************}
- FUNCTION ANSILeft(Cols : word) : string;
- VAR
- ColsStr : string;
-
- BEGIN
- str(Cols,ColsStr);
- ANSILeft := #27+'['+ColsStr+'D';
- END;
-
-
- {****************************************************************************}
- {*** ***}
- {*** Function that returns the ANSI code to change the screen color ***}
- {*** to an "Fg" foreground and a "Bg" background. ***}
- {*** ***}
- {****************************************************************************}
- FUNCTION ANSIColor(Fg, Bg : integer) : string;
- VAR
- FgStr, BgStr, Temp : string;
-
- BEGIN
- str(ColorArray[Fg mod 8] + 30, FgStr);
- str(ColorArray[Bg mod 8] + 40, BgStr);
- Temp := #27+'[';
- if Bg > 7 then
- Temp := Temp+'5;'
- else
- Temp := Temp+'0;';
- if Fg > 7 then
- Temp := Temp+'1;'
- else
- Temp := Temp+'2;';
- ANSIColor := Temp+FgStr+';'+BgStr+'m';
- END;
-
- {****************************************************************************}
- {*** ***}
- {*** Function that returns an ANSI code representing a music string ("s") ***}
- {*** ***}
- {****************************************************************************}
- FUNCTION ANSIMusic(s : string) : string;
-
- BEGIN
- ANSIMusic := #27+'[MF'+s+#14;
- END;
-
- {****************************************************************************}
- {*** ***}
- {*** Procedure that simulates BASIC's "PLAY" procedure. Will also work ***}
- {*** with ANSI codes. Taken from PC Magazine Volume 9 Number 3 ***}
- {*** ***}
- {****************************************************************************}
- PROCEDURE Play(SoundC : string);
- FUNCTION IsNumber(ch : char) : boolean;
- BEGIN
- IsNumber := (CH >= '0') AND (CH <= '9');
- END;
-
- {Converts a string to an integer}
- FUNCTION value(s : string) : integer;
- VAR
- ss, sss : integer;
- BEGIN
- Val(s, ss, sss);
- value := ss;
- END;
-
- {Plays the selected note}
- PROCEDURE sounder(key : char; flag : char);
- VAR
- old, New, new2 : Real;
- BEGIN
- adder := 1;
- old := dly;
- New := dly;
- intern := Pos(key, 'C D E F G A B')-1;
- IF (flag = '+') AND (key <> 'E') AND (key <> 'B') {See if note}
- THEN Inc(intern); {is sharped }
- IF (flag = '-') AND (key <> 'F') AND (key <> 'C')
- THEN Dec(intern); {or a flat. }
- WHILE SoundC[vari+1] = '.' DO
- BEGIN
- Inc(vari);
- adder := adder/2;
- New := New+(old*adder);
- END;
- new2 := (New/typom)*(1-typom);
- sound(Round(Exp((octave+intern/12)*Ln(2)))); {Play the note}
- Delay(Trunc(New));
- Nosound;
- Delay(Trunc(new2));
- END;
-
- {Calculate delay for a specified note length}
- FUNCTION delayer1 : integer;
- BEGIN
- numb := value(SoundC[vari+1]);
- delayer1 := Trunc((60000/(numb*min1))*typom);
- END;
-
- {Used as above, except reads a number >10}
-
- FUNCTION delayer2 : Integer;
- BEGIN
- numb := value(SoundC[vari+1]+SoundC[vari+2]);
- delayer2 := Trunc((60000/(numb*min1))*typom);
- END;
-
- BEGIN {Play}
- SoundC := SoundC+' ';
- FOR vari := 1 TO Length(SoundC) DO
- BEGIN {Go through entire string}
- SoundC[vari] := Upcase(SoundC[vari]);
- CASE SoundC[vari] OF
- {Check to see} 'C','D','E',
- {if char is a} 'F','G','A',
- {note} 'B' : BEGIN
- flager := ' ';
- dlykeep := dly;
- chartoplay := SoundC[vari];
- IF (SoundC[vari+1] = '-') OR
- (SoundC[vari+1] = '+') THEN
- {Check for flats & sharps} BEGIN
- flager := SoundC[vari+1];
- Inc(vari);
- END;
- IF IsNumber(SoundC[vari+1]) THEN
- BEGIN
- IF IsNumber(SoundC[vari+2]) THEN
- BEGIN
- test := delayer2;
- {Make sure # is legal} IF numb < 65 THEN
- dly := test;
- Inc(vari, 2);
- END
- ELSE
- BEGIN
- test := delayer1;
- {Make sure # is legal} IF numb > 0 THEN
- dly := test;
- Inc(vari);
- END;
- END;
- sounder(chartoplay, flager);
- dly := dlykeep;
- END;
- {Check for} 'O' : BEGIN
- {octave change} Inc(vari);
- CASE SoundC[vari] OF
- '-' : IF octave > 1 THEN Dec(octave);
- '+' : IF octave < 7 THEN Inc(octave);
- '1','2','3',
- '4','5','6',
- '7' : octave := value(SoundC[vari])+4;
- ELSE Dec(vari);
- END;
- END;
- {Check for a} 'L' : IF IsNumber(SoundC[vari+1]) THEN
- {change in length} BEGIN
- {for notes} IF IsNumber(SoundC[vari+2]) THEN
- BEGIN
- test := delayer2;
- IF numb < 65 THEN
- {Make sure # is legal} dly := test;
- Inc(vari, 2);
- END
- ELSE
- BEGIN
- test := delayer1;
- IF numb > 0 THEN
- {Make sure # is legal} dly := test;
- Inc(vari);
- END;
- END;
- {Check for pause} 'P' : IF IsNumber(SoundC[vari+1]) THEN
- {and it's length} BEGIN
- IF IsNumber(SoundC[vari+2]) THEN
- BEGIN
- test := delayer2;
- IF numb < 65 THEN
- {Make sure # is legal} Delay(test);
- Inc(vari, 2);
- END
- ELSE
- BEGIN
- test := delayer1;
- IF numb > 0 THEN
- {Make sure # is legal} Delay(test);
- Inc(vari);
- END;
- END;
- {Check for} 'T' : IF IsNumber(SoundC[vari+1]) AND
- {tempo change} IsNumber(SoundC[vari+2]) THEN
- BEGIN
- IF IsNumber(SoundC[vari+3]) THEN
- BEGIN
- min1 := value(SoundC[vari+1]+
- SoundC[vari+2]+SoundC[vari+3]);
- Inc(vari, 3);
- IF min1 > 255 THEN
- {Make sure # isn't too big} min1 := 255;
- END
- ELSE
- BEGIN
- min1 := value(SoundC[vari+1]+
- SoundC[vari+2]);
- IF min1 < 32 THEN
- {Make sure # isn't too small} min1 := 32;
- END;
- min1 := min1/4;
- END;
- {Check for music} 'M' : BEGIN
- {type} Inc(vari);
- CASE Upcase(SoundC[vari]) OF
- {Normal} 'N' : typom := 7/8;
- {Legato} 'L' : typom := 1;
- {Staccato} 'S' : typom := 3/4;
- END;
- END;
- END;
- END;
- END;
-
- {****************************************************************************}
- {*** ***}
- {*** Procedure to process string "s" and write its contents to the ***}
- {*** screen, interpreting ANSI codes as it goes along. ***}
- {*** ***}
- {****************************************************************************}
- PROCEDURE ANSIWrite(s : string);
- VAR
- SaveX, SaveY : byte;
- MusicStr : string;
- MusicPos : integer;
-
- {*** Procedure to process the actual ANSI sequence ***}
- PROCEDURE ProcessEsc;
- VAR
- DeleteNum : integer;
- ts : string[5];
- Num : array[0..10] of shortint;
- Color : integer;
-
- LABEL
- loop;
-
- {*** Procedure to extract a parameter from the ANSI sequence and ***}
- {*** place it in "Num" ***}
- PROCEDURE GetNum(cx : byte);
- VAR
- code : integer;
- BEGIN
- ts := '';
- WHILE (s[1] in ['0'..'9']) and (length(s) > 0) DO
- BEGIN
- ts := ts + s[1];
- Delete(s,1,1);
- END;
- val(ts,Num[cx],code)
- END;
-
- BEGIN
- IF s[2] <> '[' THEN exit;
- Delete(s,1,2);
- IF (UpCase(s[1]) = 'M') and (UpCase(s[2]) in ['F','B',#32]) THEN
- {| Added allowance for "esc[M " as a valid music prefix in line above. DDA|}
-
- {play music} BEGIN
- Delete(s,1,2);
- MusicPos := pos(#14,s);
- Play(copy(s,1,MusicPos-1));
- DeleteNum := MusicPos;
- Goto Loop;
- END;
- fillchar(Num,sizeof(Num),#0);
- GetNum(0);
- DeleteNum := 1;
- WHILE (s[1] = ';') and (DeleteNum < 11) DO
- BEGIN
- Delete(s,1,1);
- GetNum(DeleteNum);
- DeleteNum := DeleteNum + 1;
- END;
- CASE UpCase(s[1]) of
- {move up} 'A' : BEGIN
- if Num[0] = 0 THEN
- Num[0] := 1;
- WHILE Num[0] > 0 DO
- BEGIN
- GotoXY(wherex,wherey - 1);
- Num[0] := Num[0] - 1;
- END;
- DeleteNum := 1;
- END;
- {move down} 'B' : BEGIN
- if Num[0] = 0 THEN
- Num[0] := 1;
- WHILE Num[0] > 0 DO
- BEGIN
- GotoXY(wherex,wherey + 1);
- Num[0] := Num[0] - 1;
- END;
- DeleteNum := 1;
- END;
- {move right} 'C' : BEGIN
- if Num[0] = 0 THEN
- Num[0] := 1;
- WHILE Num[0] > 0 DO
- BEGIN
- GotoXY(wherex + 1,wherey);
- Num[0] := Num[0] - 1;
- END;
- DeleteNum := 1;
- END;
- {move left} 'D' : BEGIN
- if Num[0] = 0 THEN
- Num[0] := 1;
- WHILE Num[0] > 0 DO
- BEGIN
- GotoXY(wherex - 1,wherey);
- Num[0] := Num[0] - 1;
- END;
- DeleteNum := 1;
- END;
- {goto x,y} 'H',
- 'F' : BEGIN
- if (Num[0] = 0) THEN
- Num[0] := 1;
- if (Num[1] = 0) THEN
- Num[1] := 1;
- GotoXY(Num[1],Num[0]);
- DeleteNum := 1;
- END;
- {save current} 'S' : BEGIN
- {position} SaveX := wherex;
- SaveY := wherey;
- DeleteNum := 1;
- END;
- {restore} 'U' : BEGIN
- {saved position} GotoXY(SaveX,SaveY);
- DeleteNum := 1;
- END;
- {clear screen} 'J' : BEGIN
- if Num[0] = 2 THEN
- ClrScr;
- DeleteNum := 1;
- END;
- {clear from} 'K' : BEGIN
- {cursor position} ClrEOL;
- {to end of line} DeleteNum := 1;
- END;
- {change} 'M' : BEGIN
- {colors and} DeleteNum := 0;
- {attributes} WHILE (Num[DeleteNum] <> 0) or (DeleteNum = 0) DO
- BEGIN
- CASE Num[DeleteNum] of
- {all attributes off} 0 : BEGIN
- {ie. normal white on black} textattr:=7;
- {| Changed above line from "NormVideo", which only resets attributes to
- whatever the cursor attribute at startup was. Changed to textattr:=7
- since "esc[0..m" actually equals "textattr:=7". DDA|}
-
- Bold := false;
- END;
- {bold on} 1 : BEGIN
- Bold := true;
- HighVideo;
- {| Added "HighVideo" line, since "esc[1m" by itself would not otherwise
- activate "Bold". DDA|}
-
- END;
- {blink on} 5 : textattr := textattr or blink;
- {| Changed from "textattr+blink", which would turn blink off if it was
- already on. DDA|}
-
- {reverse on} 7 : textattr := ((textattr and $07) shl 4) +
- ((textattr and $70) shr 4);
- {invisible on} 8 : textattr := 0;
- {general foregrounds} 30..
- 37 : BEGIN
- color := ColorArray[Num[DeleteNum]
- - 30];
- IF Bold THEN
- color := color + 8;
- textcolor((textattr and blink)+color);
- {| Added "textattr and blink" to preserve blink status. DDA|}
-
- END;
- {general backgrounds} 40..
- 47 : textbackground(
- ColorArray[Num[DeleteNum] - 40]);
- END;
- DeleteNum := DeleteNum + 1;
- END;
- DeleteNum := 1;
- END;
- {change text} '=',
- {modes} '?' : BEGIN
- Delete(s,1,1);
- GetNum(0);
- if UpCase(s[1]) = 'H' THEN
- BEGIN
- CASE Num[0] of
- 0 : TextMode(bw40);
- 1 : TextMode(co40);
- 2 : TextMode(bw80);
- 3 : TextMode(co80);
- 4 : GraphColorMode;
- 5 : GraphMode;
- 6 : HiRes;
- 7 : TruncateLines := false;
- END;
- END;
- if UpCase(s[1]) = 'L' THEN
- if Num[0] = 7 THEN
- TruncateLines := true;
- DeleteNum := 1;
- END;
- END;
- loop: Delete(s,1,DeleteNum);
- END;
-
- BEGIN
- WHILE length(s) > 0 DO
- BEGIN
- if s[1] = #27 THEN
- ProcessEsc
- else
- BEGIN
- Write(s[1]);
- Delete(s,1,1);
- END;
- END;
- END;
-
- {****************************************************************************}
- {*** ***}
- {*** Procedure that calls ANSIWrite, then line feeds. ***}
- {*** ***}
- {****************************************************************************}
- PROCEDURE ANSIWriteLn(s : string);
- BEGIN
- ANSIWrite(s);
- WriteLn;
- END;
-
- BEGIN
- Octave := 4;
- ChartoPlay := 'N';
- Typom := 7/8;
- Min1 := 120/4;
- {| Added "/4" to be consistent with the part of the "Play" procedure
- that reads and sets the tempo. DDA|}
-
- TruncateLines := false;
- TextAttr:=7;
- {| Added above line to account for the fact that some ANSI artists just
- assume that the screen is normal white on black to start with. DDA|}
-
- END.